package com.yuzhihao.writejvm.code.t;

import com.yuzhihao.writejvm.clazz.ClassInfo;
import com.yuzhihao.writejvm.code.Operation;
import com.yuzhihao.writejvm.code.StackFrame;
import com.yuzhihao.writejvm.format.FormsEnum;
import com.yuzhihao.writejvm.format.PrintFormat;
import com.yuzhihao.writejvm.read.Parse;
import com.yuzhihao.writejvm.read.Parses;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.log4j.Log4j2;

import java.io.DataInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * tableswitch
 *
 * @author yzh
 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.tableswitch">jvmdoc</a>
 * @since 2021年12月18日12:20:59
 */
@Data
@EqualsAndHashCode(callSuper = true)
@Log4j2
public class Tableswitch extends Operation implements Parses {

    private FormsEnum fe = FormsEnum.tableswitch;

    private ClassInfo ci;

    private byte[] bytes;

    private int defaultbyte;

    private int lowbyte;

    private int highbyte;

    private List<Mapp> indexs = new ArrayList<>();

    @Override
    public StackFrame exec(StackFrame frame) {

        return frame;
    }

    /**
     * Immediately after the lookupswitch opcode, between zero and three bytes must act as padding, such that defaultbyte1 begins at an address that is a multiple of four bytes from the start of the current method (the opcode of its first instruction).
     * @param dis
     * @param ci
     * @return
     */
    @Override
    public int parse(DataInputStream dis, ClassInfo ci) {
        //计算填充字节数
        int calcfill = (super.index + 1) % 4;
        this.bytes = new byte[(calcfill == 0) ? 0 : 4 - calcfill];

        int result = 12 + this.bytes.length;
        this.setCi(ci);
        try {
            dis.readFully(bytes);

            this.defaultbyte = Parse.byteToInt(4, dis);

            this.lowbyte = Parse.byteToInt(4, dis);

            this.highbyte = Parse.byteToInt(4, dis);

            int lenght = this.highbyte - this.lowbyte + 1;

            for (int i = 1; i <= lenght; i++) {
                int value = Parse.byteToInt(4, dis) + super.index;
                indexs.add(new Mapp(i, value));
                result += 4;
            }

        } catch (IOException e) {
            log.error(e);
        }

        return result;
    }

    @Override
    public String print(String print) {
        StringBuilder builder = new StringBuilder();
        builder.append(print).append(PrintFormat.EMPTY)
                .append(super.index).append(PrintFormat.COLON)
                .append(PrintFormat.EMPTY).append(fe.name());

        while (builder.length() < PrintFormat.EMPTY_20) {
            builder.append(PrintFormat.EMPTY);
        }
        builder.append("{")
                .append(PrintFormat.EMPTY)
                .append(PrintFormat.DOUBLE_SLASH)
                .append(PrintFormat.EMPTY)
                .append(this.lowbyte)
                .append(PrintFormat.EMPTY)
                .append("to")
                .append(PrintFormat.EMPTY)
                .append(this.highbyte).append(System.lineSeparator());

        StringBuilder format = new StringBuilder(print);
        while (format.length() < (PrintFormat.EMPTY_20 - 5)) {
            format.append(PrintFormat.EMPTY);
        }

        for (Mapp mapp : this.indexs) {
            builder.append(format).append(PrintFormat.EMPTY).append(mapp.getValue())
                    .append(PrintFormat.COLON).append(PrintFormat.EMPTY)
                    .append(mapp.getTarget()).append(System.lineSeparator());
        }
        builder.append(format).append(PrintFormat.EMPTY).append("default")
                .append(PrintFormat.COLON).append(PrintFormat.EMPTY)
                .append(this.defaultbyte + super.index).append(System.lineSeparator());
        builder.append(print).append(PrintFormat.EMPTY).append(PrintFormat.MEMPTY)
                .append("}").append(System.lineSeparator());
        return builder.toString();
    }

    @Data
    @AllArgsConstructor
    public static class Mapp {
        private int value;
        private int target;
    }
}
